package cn.com.stary1993.kite.captcha.support.simple;

import cn.com.stary1993.kite.captcha.config.CaptchaProperties;
import cn.com.stary1993.kite.captcha.config.SimpleImageCaptchaProperties;
import cn.com.stary1993.kite.captcha.store.CacheStore;
import cn.com.stary1993.kite.captcha.support.CaptchaValidException;
import cn.com.stary1993.kite.captcha.support.simple.generator.SimpleImageCaptchaGenerator;
import cn.com.stary1993.kite.captcha.vo.CaptchaResponse;
import cn.com.stary1993.kite.captcha.vo.SimpleCaptchaVO;
import cn.hutool.captcha.AbstractCaptcha;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.io.OutputStream;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * 默认简单图形验证码
 *
 * @author stary1993
 */
@Slf4j
@Data
public class DefaultSimpleImageCaptcha extends SimpleImageCaptcha {

	private CaptchaProperties target;

	/**
	 * 缓冲存储.
	 */
	private CacheStore cacheStore;

	private SimpleImageCaptchaGenerator generator;

	public DefaultSimpleImageCaptcha(CacheStore cacheStore, CaptchaProperties target,
			SimpleImageCaptchaGenerator generator) {
		setCacheStore(cacheStore);
		this.target = target;
		this.generator = generator;
	}

	@Override
	public CacheStore getCacheStore() {
		return this.cacheStore;
	}

	@Override
	public void setCacheStore(CacheStore cacheStore) {
		this.cacheStore = cacheStore;
	}

	@Override
	public CaptchaResponse<SimpleCaptchaVO> generate() {
		return generate(target.getSimple().getType());
	}

	/**
	 * 生成验证码
	 * @param type type类型
	 * @return
	 */
	@Override
	public CaptchaResponse<SimpleCaptchaVO> generate(SimpleImageCaptchaType type) {
		return this.generate(type, null);
	}

	/**
	 * 生成验证码
	 * @param type 类型
	 * @param timestamp 时间戳
	 * @return CaptchaResponse<SimpleCaptchaVO>
	 */
	@Override
	public CaptchaResponse<SimpleCaptchaVO> generate(SimpleImageCaptchaType type, String timestamp) {
		if (type == null) {
			type = SimpleImageCaptchaType.spec;
		}
		AbstractCaptcha captcha = generator.getCaptcha(type);
		captcha.createCode();
		String id = generatorId();
		String code = captcha.getCode();
		cacheVerification(id, type.name(), timestamp, code);
		CaptchaResponse<SimpleCaptchaVO> response = new CaptchaResponse<>();
		response.setId(id);
		SimpleCaptchaVO captchaVO = new SimpleCaptchaVO();
		captchaVO.setId(id);
		String base64Header = Objects.equals(type, SimpleImageCaptchaType.gif) ? BASE64_GIF : BASE64_JPG;
		captchaVO.setImage(base64Header + captcha.getImageBase64());
		response.setCaptcha(captchaVO);
		return response;
	}

	/**
	 * 输出流
	 * @param out
	 */
	@Override
	public void out(String timestamp, OutputStream out) {
		SimpleImageCaptchaType type = target.getSimple().getType();
		if (type == null) {
			type = SimpleImageCaptchaType.spec;
		}
		out(type, timestamp, out);

	}

	/**
	 * 输出流
	 * @param out
	 */
	@Override
	public void out(SimpleImageCaptchaType type, String timestamp, OutputStream out) {
		AbstractCaptcha captcha = generator.getCaptcha(type);
		captcha.createCode();
		String code = captcha.getCode();
		cacheVerification(null, type != null ? type.name() : null, timestamp, code);
		captcha.write(out);
	}

	/**
	 * 验证
	 * @param inputCode 用户验证码
	 * @return
	 */
	@Override
	public boolean verify(String inputCode) {
		return this.verify(null, inputCode);
	}

	@Override
	public boolean verify(String timestamp, String inputCode) {
		SimpleImageCaptchaType type = target.getSimple().getType();
		if (type == null) {
			type = SimpleImageCaptchaType.spec;
		}
		return verify(null, type, timestamp, inputCode);
	}

	@Override
	public boolean verify(SimpleImageCaptchaType type, String timestamp, String inputCode) {
		return verify(null, type, timestamp, inputCode);
	}

	@Override
	public boolean verify(String id, SimpleImageCaptchaType type, String timestamp, String inputCode) {
		String key = getKey(id, type.name(), timestamp);
		Object cacheCode = cacheStore.getAndRemoveCache(key);
		if (Objects.isNull(cacheCode)) {
			throw new CaptchaValidException("验证码已失效");
		}
		JSONObject data = new JSONObject(cacheCode);
		log.debug("cache Captcha : {}", data);
		return this.generator.verify(data.getStr(CaptchaProperties.PREFIX), inputCode);

	}

	protected String generatorId() {
		return IdUtil.fastUUID();
	}

	/**
	 * 缓存验证码
	 * @param id
	 * @param type
	 * @param timestamp
	 * @param code
	 */
	protected void cacheVerification(String id, String type, String timestamp, String code) {
		SimpleImageCaptchaProperties prop = target.getSimple();
		JSONObject data = new JSONObject();
		data.set(CaptchaProperties.PREFIX, code);
		if (!getCacheStore().setCache(getKey(id, type, timestamp), data.toString(), prop.getExpire(),
				TimeUnit.MILLISECONDS)) {
			log.error("缓存验证码数据失败， id={}, code={}", id, code);
			throw new CaptchaValidException(type, "缓存验证码数据失败");
		}
	}

	protected String getKey(String id, String type, String timestamp) {
		SimpleImageCaptchaProperties prop = target.getSimple();
		String key = target.getPrefix().concat(":").concat(prop.getKeyPrefix());
		if (StrUtil.isNotBlank(id)) {
			key = key.concat(":").concat(id);
		}
		if (StrUtil.isNotBlank(type)) {
			key = key.concat(":").concat(type);
		}
		if (StrUtil.isNotBlank(timestamp)) {
			key = key.concat(":").concat(timestamp);
		}
		return key;
	}

}
